/** 
 * Project Name:tinysql 
 * File Name:RedisUtil.java 
 * Package Name:com.wdy.golden.tinysql.util 
 * Date:2021年3月31日上午10:03:01 
 * 
*/
package com.wdy.golden.tinysql.util;

import java.time.Duration;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.RedisNode;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisSentinelConfiguration;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettucePoolingClientConfiguration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import com.alibaba.fastjson.support.spring.FastJsonRedisSerializer;

/**
 * ClassName: RedisUtil <br/>
 * Function: TODO ADD FUNCTION. <br/>
 * Reason: TODO ADD REASON. <br/>
 * Date: 2021年3月31日 上午10:03:01 <br/>
 * 
 * @author DongYe_Wu
 * @version
 * @see
 */

public class RedisUtil {
	
    private Integer db;
    private Integer maxIdle;
    private Integer minIdle;
    private Integer maxTotal;
    private Long commandTimeout;
    private Long shutdownTimeout;
//    private String redisPassword;
//    private String sentinelPassword;
    private String sentinelMaster;
    private String sentinelNodes;

	private RedisUtil() {
	}
	
	private static class RedisHandler {
		private static RedisUtil instance = new RedisUtil();
	}

	public static RedisUtil getInstance() {
		return RedisHandler.instance;
	}
	

	
//  @Bean("tinyStringRedisTemplate")
//  @Primary
//  public StringRedisTemplate tinyStringRedisTemplate() {
//      StringRedisTemplate stringRedisTemplate = new StringRedisTemplate();
//      stringRedisTemplate.setConnectionFactory(redisConnectionFactory());
//      stringRedisTemplate.setKeySerializer(new StringRedisSerializer());//key序列化
//      stringRedisTemplate.setValueSerializer(new StringRedisSerializer());//value序列化
//      stringRedisTemplate.afterPropertiesSet();
//      return stringRedisTemplate;
//  }
  
  @Bean("tinyRedisTemplate")
  public RedisTemplate<String, Object> tinyRedisTemplate() {
      RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
      redisTemplate.setConnectionFactory(redisConnectionFactory());
      redisTemplate.setKeySerializer(new StringRedisSerializer());//key序列化
      redisTemplate.setValueSerializer(new FastJsonRedisSerializer<>(Object.class));//value序列化
      redisTemplate.afterPropertiesSet();
      return redisTemplate;
  }
  
//  @Bean("defaultTinyRedisTemplate")
//  public RedisTemplate<String, Object> defaultTinyRedisTemplate() {
//      RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
//      redisTemplate.setConnectionFactory(redisConnectionFactory());
//      redisTemplate.setDefaultSerializer(new JdkSerializationRedisSerializer());
//      redisTemplate.setKeySerializer(new StringRedisSerializer());//key序列化
//      redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());//value序列化
//      redisTemplate.afterPropertiesSet();
//      return redisTemplate;
//  }
    
    @Bean
    public LettuceConnectionFactory redisConnectionFactory() {
        return getLettuceConnectionFactory(db);
    }

    /**
     * LettuceConnectionFactory 配置
     * @return LettuceConnectionFactory
     */
    private LettuceConnectionFactory getLettuceConnectionFactory(Integer db) {
        LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory(sentinelConfig(), getLettucePoolingClientConfiguration());
        lettuceConnectionFactory.setDatabase(db);
        return lettuceConnectionFactory;
    }

    /**
     * lettuce 连接池配置
     * @return LettucePoolingClientConfiguration
     */
    private LettucePoolingClientConfiguration getLettucePoolingClientConfiguration() {
        LettucePoolingClientConfiguration.LettucePoolingClientConfigurationBuilder builder = LettucePoolingClientConfiguration.builder();
        GenericObjectPoolConfig<?> genericObjectPoolConfig = new GenericObjectPoolConfig<Object>();
        genericObjectPoolConfig.setMaxTotal(maxTotal);
        genericObjectPoolConfig.setMaxIdle(maxIdle);
        genericObjectPoolConfig.setMinIdle(minIdle);
        genericObjectPoolConfig.setLifo(true);
        genericObjectPoolConfig.setFairness(false);
        genericObjectPoolConfig.setMaxWaitMillis(-1);
        genericObjectPoolConfig.setMinEvictableIdleTimeMillis(-1);
        genericObjectPoolConfig.setSoftMinEvictableIdleTimeMillis(-1);
        genericObjectPoolConfig.setNumTestsPerEvictionRun(3);
        genericObjectPoolConfig.setTestOnCreate(false);
        genericObjectPoolConfig.setTestOnBorrow(false);
        genericObjectPoolConfig.setTestOnReturn(false);
        genericObjectPoolConfig.setTestWhileIdle(false);
        genericObjectPoolConfig.setTimeBetweenEvictionRunsMillis(-1);
        genericObjectPoolConfig.setBlockWhenExhausted(true);
        builder.poolConfig(genericObjectPoolConfig);
        builder.commandTimeout(Duration.ofSeconds(commandTimeout));
        builder.shutdownTimeout(Duration.ofMillis(shutdownTimeout));
        return builder.build();
    }

    private RedisSentinelConfiguration sentinelConfig() {
        RedisSentinelConfiguration sentinelConfig = new RedisSentinelConfiguration();
        sentinelConfig.setMaster(sentinelMaster);
//        sentinelConfig.setPassword(redisPassword);
        sentinelConfig.setPassword(RedisPassword.none());
        String[] sentinels = sentinelNodes.split("\\|");
        List<RedisNode> list = new ArrayList<>();
        for (String sentinel : sentinels) {
            String[] nodes = sentinel.split(":");
            list.add(new RedisNode(nodes[0], Integer.parseInt(nodes[1])));
        }
        sentinelConfig.setSentinels(list);
//        sentinelConfig.setSentinelPassword(RedisPassword.of(sentinelPassword));
        sentinelConfig.setSentinelPassword(RedisPassword.none());
        return sentinelConfig;
    }

    
    
    
	public Integer getDb() {
		return db;
	}

	public RedisUtil setDb(Integer db) {
		this.db = db;
		return this;
	}

	public Integer getMaxIdle() {
		return maxIdle;
	}

	public RedisUtil setMaxIdle(Integer maxIdle) {
		this.maxIdle = maxIdle;
		return this;
	}

	public Integer getMinIdle() {
		return minIdle;
	}

	public RedisUtil setMinIdle(Integer minIdle) {
		this.minIdle = minIdle;
		return this;
	}

	public Integer getMaxTotal() {
		return maxTotal;
	}

	public RedisUtil setMaxTotal(Integer maxTotal) {
		this.maxTotal = maxTotal;
		return this;
	}

	public Long getCommandTimeout() {
		return commandTimeout;
	}

	public RedisUtil setCommandTimeout(Long commandTimeout) {
		this.commandTimeout = commandTimeout;
		return this;
	}

	public Long getShutdownTimeout() {
		return shutdownTimeout;
	}

	public RedisUtil setShutdownTimeout(Long shutdownTimeout) {
		this.shutdownTimeout = shutdownTimeout;
		return this;
	}

	public String getSentinelMaster() {
		return sentinelMaster;
	}

	public RedisUtil setSentinelMaster(String sentinelMaster) {
		this.sentinelMaster = sentinelMaster;
		return this;
	}

	public String getSentinelNodes() {
		return sentinelNodes;
	}

	public RedisUtil setSentinelNodes(String sentinelNodes) {
		this.sentinelNodes = sentinelNodes;
		return this;
	}
    
}
